package 多线程.AtomicInteger原理用法;

import sun.awt.windows.ThemeReader;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 意思就是说，如果⼀个变量加了volatile关键字，就会告诉编译器和JVM的内存模型：这个变量是对所有线程共享的、可见的，每次jvm都会
 * 读取最新写⼊的值并使其最新值在所有CPU可见。volatile似乎是有时候可以代替简单的锁，似乎加了volatile关键字就省掉了锁。但⼜说
 * volatile不能保证原⼦性（java程序员很熟悉这句话：volatile仅仅⽤来保证该变量对所有线程的可见性，但不保证原⼦性）。这不是互相⽭盾
 * 吗？
 * 不要将volatile⽤在getAndOperate场合，仅仅set或者get的场景是适合volatile的
 * 不要将volatile⽤在getAndOperate场合（这种场合不原⼦，需要再加锁），仅仅set或者get的场景是适合volatile的。
 * volatile没有原⼦性举例：AtomicInteger⾃增
 * 例如你让⼀个volatile的integer⾃增（i++），其实要分成3步：1）读取volatile变量值到local； 2）增加变量的值；3）把local的值写回，让
 * 其它的线程可见。这3步的jvm指令为：
 * mov    0xc(%r10),%r8d ; Load
 * inc    %r8d           ; Increment
 * mov    %r8d,0xc(%r10) ; Store
 * lock addl $0x0,(%rsp) ; StoreLoad Barrier
 * 注意最后⼀步是内存屏障
 *
 *
 * volatile 相当于只是可见性，但是AtomicInteger底层变量也是volatile修饰的，为什么它可以保证原子性呢
 *
 * volatile没有原⼦性举例：singleton单例模式实现
 * 这是⼀段线程不安全的singleton（单例模式）实现，尽管使⽤了volatile：
 * public class wrongsingleton {
 *     private static volatile wrongsingleton _instance = null;
 *     private wrongsingleton() {}
 *     public static wrongsingleton getInstance() {
 *         if (_instance == null) {
 *             _instance = new wrongsingleton();
 *         }
 *         return _instance;
 *     }
 * }
 * volatile保证变量对线程的可见性，但不保证原⼦性。
 * 为什么AtomicXXX具有原⼦性和可见性？
 * 就拿AtomicLong来说，它既解决了上述的volatile的原⼦性没有保证的问题，⼜具有可见性。它是如何做到的？当然就是上⽂《》提到的
 * CAS（⽐较并交换）指令。其实AtomicLong的源码⾥也⽤到了volatile，但只是⽤来读取或写⼊，见源码：
 * public class AtomicLong extends Number implements java.io.Serializable {
         private volatile long value;

         public AtomicLong(long initialValue) {
         value = initialValue;
         }
         public AtomicLong() {
         }

        *其CAS源码核⼼代码为：
         int compare_and_swap (int* reg, int oldval, int newval)
         {
         ATOMIC();
         int old_reg_val = *reg;
         if (old_reg_val == oldval)
         *reg = newval;
         END_ATOMIC();
         return old_reg_val;
         }

        *虚拟机指令为：

         mov    0xc(%r11),%eax       ; Load
         mov    %eax,%r8d
         inc    %r8d                 ; Increment
         lock cmpxchg %r8d,0xc(%r11) ; Compare and exchange

        *因为CAS是基于乐观锁的，也就是说当写⼊的时候，如果寄存器旧值已经不等于现值，说明有其他CPU在修改，那就继续尝试。所以这就
        *保证了操作的原⼦性。
 *
 *      https://www.cnblogs.com/Mainz/p/3556430.html
 *      建议还是看看这个，这个博客写的很好
 *
 */
public class SharedVariableExample {
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.incrementAndGet(); // 原子递增操作
            }
        };

        for (int i = 0; i < 500; i++) {
            Thread thread1 = new Thread(task);
            thread1.start();
            thread1.join();
        }
//        Thread thread1 = new Thread(task);
//        Thread thread2 = new Thread(task);
//
//        thread1.start();
//        thread2.start();
//
//        thread1.join();
//        thread2.join();
//        Thread.sleep(1000);
        System.out.println("Counter value: " + counter.get());
    }
}
